package pong.mathematic;

import java.util.Random;
import java.util.Vector;

import pong.game.data.Data;
import pong.game.objects.GL_ActiveItem;
import pong.game.objects.GL_Handle;
import pong.game.objects.GL_Washer;
import pong.game.user.Player;
import android.util.Log;
import android.util.Pair;

public class Engine {

	private static Engine instance = null;
	private static final String TAG = "Engine";

	private static final float DEFAULT_WORLD_SIZE = 10f;

	private static final float DISTANCE_TO_COLLISION = 0.0f; // 0.2f

	private static final float CAMERA_MIN_GRAVITY_HEIGHT = 3f; // 13
	private static final float CAMERA_MAX_GRAVITY_HEIGHT = 100f; // 37 // 28

	private static final float CAMERA_MIN_HEIGHT = CAMERA_MIN_GRAVITY_HEIGHT - 2;
	private static final float CAMERA_MAX_HEIGHT = CAMERA_MAX_GRAVITY_HEIGHT + 0;

	private static final boolean CAMERA_IGNORE_SCALE_IN_MIN_MAGNETIC_ZONE = true;
	private static final boolean CAMERA_IGNORE_SCALE_IN_MAX_MAGNETIC_ZONE = false;

	private static final float CAMERA_MAX_SCALE_SPEED = 80f;
	private static final float CAMERA_SCALE_SLOWER = 9f;

	private static final float CAMERA_SPEED_TO_TOUCH_OBJECT = 0.7f;
	private static final float CAMERA_MOVE_SLOWER = 5f;
	private static final float CAMERA_MOVE_SENSITIVITY = 5.88f;
	private static final float CAMERA_SCALE_SENSITIVITY = 6f;
	private static final float CAMERA_POWER_RETURN_TO_ZONE = 25f; // 25
	private static final float CAMERA_POWER_GRAVITY_SCALE = 20f; // 50
	private static final float CAMERA_AUTO_SCALE_POWER = 1f; // 16
	private static final float CAMERA_PERIMETER_BARIER = 0f; // 2
	private static final float CAMERA_PROCENT_BOUNCE_WALL = 0.5f;
	private static final float CAMERA_ADD_TO_WATCH_ZONE = 3f;

	private static final float CONTENT_ITEM_WORLD_BARIER = 10f; // 13
	private static final float COLLIDE_IMPULSE_POWER = 15f;
	private static final float IMPULSE_MULTY_COLLIDE_TO_LOGO = 0.2f;

	private static final float IMPULSE_PUSH_SENSATIVITY = 30f;

	private static final float SMALL_VALUE = 0.001f;

	private static final float ITEM_SENSITIVITY = 1.5f;
	private static final float WASHER_MAX_SPEED = 0.5f;

	public static final float TOUCH_ITEM_BOUNCE = 0.0f;

	private static boolean isRun = false;

	private boolean touchObjectMoved = false;
	private boolean cameraMoveToTouchObject = false;
	private boolean touchObjectPositionFixed = false;
	private boolean itemMovedWorldFrom25Precent = false;
	private final boolean dontMove = false;

	private int touchObjectIndex = -1;
	private int widthScreen = 0;
	private int heightScreen = 0;

	private long curTime = 0;
	private long timePassed = 0;
	private long lastTime = 0;

	private float timeFraction = 0.0f;
	private float widthWorld = 0;
	private float heightWorld = 0;
	private float viewRatio = 0;
	private float touchBounceItemValue = 0;

	private float cameraMaxGravityHeight = CAMERA_MAX_GRAVITY_HEIGHT;
	private float cameraMaxHeight = CAMERA_MAX_HEIGHT;

	private final Vector<Vector2D> constForces = new Vector<Vector2D>();
	private final Vector2D touchItemOffSet = new Vector2D();
	private Vector2D lastTouchPosition = null;
	private Vector2D firstTouchPosition = null;
	private final Vector2D lastTouchMonitorPosition = new Vector2D();
	private final Vector2D firstTouchMonitorPosition = new Vector2D();

	private final Rectangle camZone = new Rectangle();
	private final Rectangle worldBarrier = new Rectangle();
	private final Rectangle camWatchZone = new Rectangle();

	private static Random random = new Random();

	// private int monitorHeight = 0;
	// private int monitorWidth = 0;

	private Engine() {
		curTime = System.currentTimeMillis();
		this.updateTime();
		// this.updatePlaneWatchZone();
		// this.updateworldBarrier();
		this.updateCameraWatchZone();
		initializeConstForces();
	}

	public static Engine getInstance() {
		if (instance == null) {
			instance = new Engine();
			instance.initWorld();
		}
		return instance;
	}

	public void run() {

		// while (true) {
		//
		// if (isRun) {

		// ----- First
		updateTime();
		applyConstForces();
		sumForces();
		// moveAndUpdateBgItems();
		// generateBgBubbles();
		// moveBubbles();
		updatePlaneWatchZone();
		// updateworldBarrier();

		// ----- Second

		// second (old)
		boolean isCollide = false;
		isCollide = moveWasherItems() ? true : isCollide;
		isCollide = moveHandleItems() ? true : isCollide;
		impulseCalc();
		// first (old)
		moveCamera();
		// moveLogo();

		// ----- Third
		isCollide = checkCollisions() ? true : isCollide;

		if (isCollide)
			Data.getInstance().sendWifiData();

		// } else {
		// try {
		// Thread.sleep(SLEEP_TIME);
		// } catch (InterruptedException e) {
		// e.printStackTrace();
		// }
		// }
		// }

	}

	private void impulseCalc() {
		Data data = Data.getInstance();

		int count = data.countHandleItems();
		int countWashers = data.countWasherItems();
		for (int q = 0; q < count; q++) {
			Player player = (Player) data.getHandleItem(q);
			GL_ActiveItem impulse = player.getImpulse();

			if (impulse.isAlphaAnimation()) {

				for (int w = 0; w < countWashers; w++) {
					GL_Washer item = data.getWasherItem(w);

					float distance = impulse.getCenter().distance(item.getCenter());
					distance -= item.getRadius();

					if (distance <= impulse.getRadius()) {
						Vector3D power = new Vector3D(item.getCenter());

						power.minus(impulse.getCenter());

						float impulsePower = power.distance() - item.getRadius();
						power.normalize();
						power.multy(impulsePower * IMPULSE_PUSH_SENSATIVITY * timeFraction);

						item.getDirection().add(power);
					}
				}
			}
		}
	}

	private void updatePlaneWatchZone() {
		camZone.setRectangle(worldBarrier);
	}

	public void sensorUpdate(float x, float y) {
		// TODO: continue;
	}

	private void updateCameraWatchZone() {
		camWatchZone.getUpLeft().setVector(
				getTouchPosition(widthScreen * 0.055f, heightScreen * 0.055f, widthScreen, heightScreen, viewRatio));
		camWatchZone.getDownRight().setVector(
				getTouchPosition(widthScreen * 0.945f, heightScreen * 0.945f, widthScreen, heightScreen, viewRatio));

		// swap
		// float temp = camWatchZone.getDownRight().getY();
		// camWatchZone.getDownRight().setY(camWatchZone.getUpLeft().getY());
		// camWatchZone.getUpLeft().setY(temp);
	}

	public void cameraScale(float scaleValue) {
		Vector3D camDir = Data.getInstance().getCamera().getDirection();
		Vector3D camPos = Data.getInstance().getCamera().getPosition();

		returnNormalBounce();

		if (CAMERA_IGNORE_SCALE_IN_MIN_MAGNETIC_ZONE && camPos.getZ() < CAMERA_MIN_GRAVITY_HEIGHT) {
			return;
		}
		if (CAMERA_IGNORE_SCALE_IN_MAX_MAGNETIC_ZONE && camPos.getZ() > cameraMaxGravityHeight) {
			return;
		}
		camDir.setZ(camDir.getZ() + scaleValue * CAMERA_SCALE_SENSITIVITY);
	}

	public static boolean isRun() {
		return Engine.isRun;
	}

	private Vector2D planeCollision(Vector3D from, Vector2D clickPos, int width, int height, float ratio) {

		from = new Vector3D(from);
		float alpha = 22.5f / (ratio > 1f ? ratio : 1f);
		// alpha = ratio;
		float h = (float) Math.cos(Math.toRadians(alpha)) * 5f / (ratio > 1f ? ratio : 1f);

		Vector3D vec = new Vector3D((2.0f * (clickPos.getX() / width) - 1.0f) / h,
				(-2.0f * (clickPos.getY() / height) + 1.0f) / (ratio * h), 1);

		vec.normalize();

		float length = from.getZ() / vec.getZ();

		vec.multy(length);
		from.minus(vec);
		from.multy(-1);

		return new Vector2D(from.getX(), from.getY());
	}

	public Vector2D getTouchPosition(float x, float y, int width, int height, float ratio) {
		Vector3D camPos = new Vector3D(Data.getInstance().getCamera().getPosition());
		// camPos.setZ(-camPos.getZ());

		return planeCollision(camPos, new Vector2D(x, y), width, height, ratio);
	}

	public void touchDown(float x, float y, int width, int height) {

		// monitorHeight = height;
		// monitorWidth = width;

		// if (isIgnoreTouch()) {
		// return;
		// }

		Data.getInstance().getPlayer().impulse();

		itemMovedWorldFrom25Precent = false;
		cameraMoveToTouchObject = false;
		setTouchObjectIndex(-1);

		lastTouchMonitorPosition.setX(x);
		lastTouchMonitorPosition.setY(y);

		// Log.v(TAG, "Touch event: touchDown");

		this.firstTouchMonitorPosition.setX(x);
		this.firstTouchMonitorPosition.setY(y);
		this.firstTouchPosition = getTouchPosition(x, y, width, height, viewRatio);
		this.lastTouchPosition = new Vector2D(firstTouchPosition);

		Log.i(TAG, "firstTouch = " + firstTouchPosition.toString());

		returnNormalBounce();

		setTouchObjectIndex(-1);

		Data data = Data.getInstance();
		for (int q = 0; q < data.countTouchItems(); q++) {
			GL_ActiveItem item = data.getTouchItem(q);
			if (item.getAlpha() != 0f && item.isIn(lastTouchPosition)) {

				// Log.d(TAG, "ID=" + item.getID());

				touchItemOffSet.setX(firstTouchPosition.getX());
				touchItemOffSet.setY(firstTouchPosition.getY());
				touchItemOffSet.minus(item.getCenter());

				setTouchObjectIndex(q);
				setTouchBounce(TOUCH_ITEM_BOUNCE);

				break;
			}
		}
	}

	public void touchMove(float x, float y, float speedX, float speedY, int width, int height) {

		// monitorHeight = height;
		// monitorWidth = width;

		// if (isIgnoreTouch()) {
		// return;
		// }

		lastTouchMonitorPosition.setX(x);
		lastTouchMonitorPosition.setY(y);

		// Log.v(TAG, "Touch event: touchMove");

		if (this.getTouchObjectIndex() < 0) {
			Vector3D power = new Vector3D(speedX, speedY, 0);
			power.setX(power.getX() * width / height);

			power.multy(CAMERA_MOVE_SENSITIVITY * -(Data.getInstance().getCamera().getPosition().getZ()));

			Vector3D camDir = Data.getInstance().getCamera().getDirection();
			if (!dontMove) {
				camDir.add(power);
			}
			returnNormalBounce();

		} else {
			touchObjectMoved = true;
			lastTouchPosition = getTouchPosition(x, y, width, height, viewRatio);
		}
	}

	public void touchUp(boolean isMultyTouch) {

		// if (isIgnoreTouch()) {
		// return;
		// }

		// Log.v(TAG, "Touch event: touchUp");

		if (!touchObjectMoved && !isMultyTouch) {
			if ((this.getTouchObjectIndex() >= 0) && !touchObjectPositionFixed) {
				Data data = Data.getInstance();
				data.touchItem(this.getTouchObjectIndex());
				cameraMoveToTouchObject = true;
				touchObjectPositionFixed = true;
				return;
			}
		}

		returnNormalBounce();

		firstTouchPosition = null;
		lastTouchPosition = null;
		touchObjectPositionFixed = false;
		touchObjectMoved = false;

		if (itemMovedWorldFrom25Precent) {
			cameraMoveToTouchObject = true;
		} else {
			setTouchObjectIndex(-1);
		}

	}

	private boolean moveHandleItems() {
		Data data = Data.getInstance();
		boolean isCollide = false;

		int count = data.countHandleItems();
		for (int q = 0; q < count; q++) {
			Player item = (Player) data.getHandleItem(q);

			item.moveEnts(Float.MAX_VALUE, timeFraction);

			isCollide = checkWallCollisionsHandle(item) ? true : isCollide;

			if (isCollide)
				item.setIsNeedSynchronize(true);
		}
		return isCollide;
	}

	private boolean moveWasherItems() {
		Data data = Data.getInstance();
		boolean isCollide = false;

		int count = data.countWasherItems();
		for (int q = 0; q < count; q++) {
			GL_Washer item = data.getWasherItem(q);

			item.moveEnts(WASHER_MAX_SPEED, timeFraction);

			isCollide = checkWallCollisionsWasher(item) ? true : isCollide;

			if (isCollide)
				item.setIsNeedSynchronize(true);
		}
		return isCollide;
	}

	public void setTouchBounce(float value) {
		returnNormalBounce();

		if (getTouchObjectIndex() < 0 || getTouchObjectIndex() >= Data.getInstance().countTouchItems()) {
			return;
		}
		GL_ActiveItem item = Data.getInstance().getTouchItem(getTouchObjectIndex());

		if (item != null) {
			setTouchBounceItemValue(item.getBounce());
			item.setBounce(value);
		}
	}

	public void returnNormalBounce() {
		returnNormalBounce(getTouchObjectIndex());
	}

	public void returnNormalBounce(int index) {
		if (index < 0 || index >= Data.getInstance().countTouchItems()) {
			return;
		}
		GL_ActiveItem item = Data.getInstance().getTouchItem(index);
		if (item != null && item.getBounce() == TOUCH_ITEM_BOUNCE) {
			item.setBounce(getTouchBounceItemValue());
		}
		setTouchBounceItemValue(0);
	}

	public void updateTime() {
		lastTime = curTime;
		curTime = System.currentTimeMillis();
		timePassed = (curTime - lastTime);
		timeFraction = (timePassed / 1000.0f);
		if (timeFraction > 1f) {
			timeFraction = 1f;
		}
		if (timeFraction == 0) {
			timeFraction = 0.00000001f;
		}
	}

	private void initializeConstForces() {
		constForces.add(new Vector2D(0.0f, -0.12f)); // GRAVITY
	}

	private void applyConstForces() {
		Vector2D accel = new Vector2D();
		// Find the total acceleration of all const forces.
		for (int q = 0; q < constForces.size(); q++) {
			accel.add(constForces.get(q));
		}
		// Apply the sum acceleration to each entity.
		Data data = Data.getInstance();
		int count = data.countTouchItems();
		for (int q = 0; q < count; q++) {
			if (touchObjectPositionFixed && q == getTouchObjectIndex()) {
				continue;
			}
			GL_ActiveItem item = Data.getInstance().getTouchItem(q);
			if (item != null) {
				item.addAccel(accel);
			}
		}
	}

	private void sumForces() {
		Data data = Data.getInstance();
		int count = data.countWasherItems();
		for (int q = 0; q < count; q++) {
			// if (touchObjectPositionFixed && q == getTouchObjectIndex()) {
			// continue;
			// }
			GL_ActiveItem item = data.getWasherItem(q);
			sumForces(item);
		}
		count = data.countHandleItems();
		for (int q = 0; q < count; q++) {
			// if (touchObjectPositionFixed && q == getTouchObjectIndex()) {
			// continue;
			// }
			GL_ActiveItem item = data.getHandleItem(q);
			sumForces(item);
		}
	}

	private void sumForces(GL_ActiveItem item) {
		// Get the sum of all accelerations acting on object.
		Vector2D theAccel = item.sumAccel();
		// Apply the resulting change in velocity.
		Vector3D direction = item.getDirection();

		if (theAccel != null)
			direction.setXYZ(direction.getX() + (theAccel.getX() * timeFraction), direction.getY()
					+ (theAccel.getY() * timeFraction), direction.getZ());

		// Apply drag coefficient
		item.applyDrag(1.0f - (timeFraction * item.getDrag()));

		item.applyCollideImpulseDrag(1.0f - (timeFraction * item.getCollideImpulseDrag()));
	}

	private boolean checkWallCollisionsHandle(GL_Handle item) {
		float maxY = worldBarrier.getUpLeft().getY();
		float maxX = worldBarrier.getDownRight().getX() - item.getRadius();
		float minY = worldBarrier.getDownRight().getY();
		float minX = worldBarrier.getUpLeft().getX() + item.getRadius();

		boolean isTopParty = item.getCenter().getY() < worldBarrier.getBottom()
				+ (worldBarrier.getTop() - worldBarrier.getBottom()) / 2;

		boolean isCollide = false;
		float distance = item.getCenter().getY() - maxY;

		if (distance >= 0 || !isTopParty) {
			item.getDirection().setY(0);
			item.getCenter().setY(maxY);
			// item.setDirection(item.getDirection().getX(),
			// item.getDirection().getY() + (item.getCenter().getY() - maxY)
			// * -item.getBounceWall());
			isCollide = true;
		}

		distance = item.getCenter().getY() - minY;

		if (distance <= 0 || isTopParty) {
			item.getDirection().setY(0);
			item.getCenter().setY(minY);
			// item.setDirection(item.getDirection().getX(),
			// item.getDirection().getY() + (minY + item.getCenter().getY())
			// * -item.getBounceWall());
			isCollide = true;
		}

		distance = item.getCenter().getX() - maxX;

		if (distance >= 0) {
			item.getDirection().setX(0);
			item.getCenter().setX(maxX);
			// item.setDirection(item.getDirection().getX() +
			// (item.getCenter().getX() - maxX) * -item.getBounceWall(),
			// item.getDirection().getY());
			isCollide = true;
		}

		distance = item.getCenter().getX() - minX;

		if (distance <= 0) {
			item.getDirection().setX(0);
			item.getCenter().setX(minX);
			// item.setDirection(item.getDirection().getX() + (minX +
			// item.getCenter().getX()) * -item.getBounceWall(),
			// item.getDirection().getY());
			isCollide = true;
		}

		return isCollide;
	}

	private boolean checkWallCollisionsWasher(GL_Washer item) {

		float maxY = worldBarrier.getUpLeft().getY() - item.getRadius();
		float maxX = worldBarrier.getDownRight().getX() - item.getRadius();
		float minY = worldBarrier.getDownRight().getY() + item.getRadius();
		float minX = worldBarrier.getUpLeft().getX() + item.getRadius();

		boolean isCollide = false;
		float distance = item.getCenter().getY() - maxY;

		if (distance >= 0) {
			item.getDirection().setY(-Math.abs(item.getDirection().getY()) * item.getBounceWall());
			item.getCenter().setY(maxY - distance);
			// item.setDirection(item.getDirection().getX(),
			// item.getDirection().getY() + (item.getCenter().getY() - maxY)
			// * -item.getBounceWall());
			item.topCollide();
			isCollide = true;
		}

		distance = item.getCenter().getY() - minY;

		if (distance <= 0) {
			item.getDirection().setY(Math.abs(item.getDirection().getY()) * item.getBounceWall());
			item.getCenter().setY(minY - distance);
			// item.setDirection(item.getDirection().getX(),
			// item.getDirection().getY() + (minY + item.getCenter().getY())
			// * -item.getBounceWall());
			item.bottomCollide();
			isCollide = true;
		}

		distance = item.getCenter().getX() - maxX;

		if (distance >= 0) {
			item.getDirection().setX(-Math.abs(item.getDirection().getX()) * item.getBounceWall());
			item.getCenter().setX(maxX - distance);
			// item.setDirection(item.getDirection().getX() +
			// (item.getCenter().getX() - maxX) * -item.getBounceWall(),
			// item.getDirection().getY());
			isCollide = true;
		}

		distance = item.getCenter().getX() - minX;

		if (distance <= 0) {
			item.getDirection().setX(Math.abs(item.getDirection().getX()) * item.getBounceWall());
			item.getCenter().setX(minX - distance);
			// item.setDirection(item.getDirection().getX() + (minX +
			// item.getCenter().getX()) * -item.getBounceWall(),
			// item.getDirection().getY());
			isCollide = true;
		}
		return isCollide;
	}

	private boolean checkCollisions() {
		boolean isCollide = false;
		Data data = Data.getInstance();
		int count = data.countWasherItems();
		for (int q = 0; q < count; q++) {
			GL_ActiveItem item1 = data.getWasherItem(q);
			if (item1 == null) {
				continue;
			}

			for (int w = q + 1; w < count; w++) {
				GL_ActiveItem item2 = data.getWasherItem(w);

				if (item2 == null)
					continue;

				isCollide = collide(item1, item2) ? true : isCollide;

				if (isCollide) {
					item1.setIsNeedSynchronize(true);
					item2.setIsNeedSynchronize(true);
				}
			}

			int countHandles = data.countHandleItems();

			for (int w = 0; w < countHandles; w++) {
				GL_ActiveItem item2 = data.getHandleItem(w);

				if (item2 == null)
					continue;

				isCollide = collide(item1, item2) ? true : isCollide;

				if (isCollide)
					item2.setIsNeedSynchronize(true);
			}
		}
		return isCollide;
	}

	private boolean collide(GL_ActiveItem item1, GL_ActiveItem item2) {

		Vector3D center1 = item1.getCenter();
		Vector3D center2 = item2.getCenter();

		float distBetweenItemToItem = center1.distance(center2);

		boolean result = false;

		if (distBetweenItemToItem < (item1.getRadius() + item2.getRadius() + DISTANCE_TO_COLLISION)) {
			result = true;
			Pair positions = new Pair(new Vector3D(), new Vector3D());
			Pair directions = new Pair(new Vector3D(), new Vector3D());
			collideMove(item1, item2, distBetweenItemToItem, positions, directions);

			item1.getCollideImpulse().add((Vector3D) directions.first);
			item2.getCollideImpulse().add((Vector3D) directions.second);

			float distance = item1.getDirection().distance();
			distance += item2.getDirection().distance();
			distance /= 2;

			((Vector3D) directions.first).normalize();
			((Vector3D) directions.second).normalize();

			((Vector3D) directions.first).multy(distance);
			((Vector3D) directions.second).multy(distance);

			item1.getDirection().add((Vector3D) directions.first);
			item2.getDirection().add((Vector3D) directions.second);

			center1.add((Vector3D) positions.first);
			center2.add((Vector3D) positions.second);
		}

		return result;
	}

	private void collideMove(GL_ActiveItem item1, GL_ActiveItem item2, float distBetween, Pair positions,
			Pair directions) {
		distBetween += SMALL_VALUE;
		Vector3D center2 = item2.getCenter(), center1 = item1.getCenter();
		float relX = center2.getX() - center1.getX();
		float relY = center2.getY() - center1.getY();
		// Take the arctan to find the collision angle.
		float collisionAngle = (float) Math.atan2(relY, relX);
		// if (collisionAngle < 0) collisionAngle += 2 * Math.PI;
		// Rotate the coordinate systems for each object's velocity to align
		// with the collision angle. We do this by supplying the collision angle
		// to the vector's rotateCoordinates method.
		TempVector2D vel2 = item2.velVector(), vel1 = item1.velVector();
		vel2.rotateCoordinates(collisionAngle);
		vel1.rotateCoordinates(collisionAngle);
		// In the collision coordinate system, the contact normals lie on the
		// x-axis. Only the velocity values along this axis are affected. We can
		// now apply a simple 1D momentum equation where the new x-velocity of
		// the first object equals a negative times the x-velocity of the
		// second.
		float swap = vel2.x;
		vel2.x = vel1.x;
		vel1.x = swap;
		// Now we need to get the vectors back into normal coordinate space.
		vel2.restoreCoordinates();
		vel1.restoreCoordinates();

		// Give each object its new velocity.
		// s.setDirection(sVel.x * s.getBounce(), (sVel.y * s.getBounce()));
		// t.setDirection((tVel.x * t.getBounce()), (tVel.y * t.getBounce()));
		// Back them up in the opcenterite angle so they are not overlapping.
		float minDist = item2.getRadius() + item1.getRadius() + DISTANCE_TO_COLLISION;
		float overlap = minDist - distBetween;
		float toMove = overlap;
		if (item2.getBounce() != TOUCH_ITEM_BOUNCE && item1.getBounce() != TOUCH_ITEM_BOUNCE) {
			toMove /= 2;
		}

		if ((item1.getBounce() != TOUCH_ITEM_BOUNCE || item2.getBounce() == TOUCH_ITEM_BOUNCE)
				|| (item1.getBounce() != TOUCH_ITEM_BOUNCE && item1.getScaleValue() > 1f)) {

			((Vector3D) directions.first).setX((float) (-toMove * Math.cos(collisionAngle) * COLLIDE_IMPULSE_POWER)); // -vel1.x
			// *
			// item1.getBounce());
			((Vector3D) directions.first).setY((float) (-toMove * Math.sin(collisionAngle) * COLLIDE_IMPULSE_POWER)); // -vel1.y
			// *
			// item1.getBounce());

			((Vector3D) positions.first).setX((float) (-toMove * Math.cos(collisionAngle)));
			((Vector3D) positions.first).setY((float) (-toMove * Math.sin(collisionAngle)));
		}

		if (item2.getBounce() != TOUCH_ITEM_BOUNCE) {

			((Vector3D) directions.second).setX((float) (toMove * Math.cos(collisionAngle) * COLLIDE_IMPULSE_POWER)); // -vel2.x
			// *
			// item2.getBounce());
			((Vector3D) directions.second).setY((float) (toMove * Math.sin(collisionAngle) * COLLIDE_IMPULSE_POWER)); // -vel2.y
			// *
			// item2.getBounce());

			((Vector3D) positions.second).setX((float) (toMove * Math.cos(collisionAngle)));
			((Vector3D) positions.second).setY((float) (toMove * Math.sin(collisionAngle)));
		}
	}

	public void initWorld() {
		initWorld(DEFAULT_WORLD_SIZE, DEFAULT_WORLD_SIZE);
	}

	public void initWorld(float width, float height) {
		setWidthWorld(width / 2);
		setHeightWorld(height / 2);

		curTime = System.currentTimeMillis();
		initializeConstForces();
	}

	public void setCameraFirstPosition(float ratio, int width, int height) {
		if (width <= 0 || height <= 0) {
			Log.e(TAG, "!!! widthScreen or heightScreen <= 0");
			return;
		}
		if (widthWorld <= 0 || heightWorld <= 0) {
			Log.e(TAG, "!!! widthWorld or heightWorld <= 0");
			return;
		}

		Vector3D camPos = Data.getInstance().getCamera().getPosition();

		// updatePlaneWatchZone();
		Vector2D upLeft = getTouchPosition(0, 10, width, height, ratio);

		float leftPower = 0;
		float upPower = 0;

		leftPower = camZone.getUpLeft().getX() - upLeft.getX();
		upPower = upLeft.getY() - camZone.getUpLeft().getY();

		// Log.v(TAG, "to camPos = " + camPos.toString());
		camPos.setXYZ(camPos.getX() - leftPower, camPos.getY() + upPower, camPos.getZ());
		// Log.v(TAG, "do camPos = " + camPos.toString());
	}

	private void returnCameraToZoneScale() {

		Vector3D camPos = Data.getInstance().getCamera().getPosition();
		Vector3D camDir = Data.getInstance().getCamera().getDirection();
		// camPos.setZ(-camPos.getZ());

		// if (leftPower != 0 && rightPower != 0 || upPower != 0 && downPower !=
		// 0) {
		// float distance = leftPower + rightPower + upPower + downPower;
		// distance *= timeFraction * CAMERA_AUTO_SCALE_POWER;
		// cameraMaxGravityHeight += distance;
		// cameraMaxHeight += distance;
		//
		// if (cameraMaxGravityHeight > CAMERA_MIN_GRAVITY_HEIGHT) {
		// cameraMaxGravityHeight = CAMERA_MIN_GRAVITY_HEIGHT - 1f;
		// cameraMaxHeight = cameraMaxGravityHeight - (CAMERA_MAX_GRAVITY_HEIGHT
		// - CAMERA_MAX_HEIGHT) - 1f;
		// }
		// // Log.v(TAG, "tag cameraMaxGravityHeight = " +
		// // cameraMaxGravityHeight);
		// } else {
		// cameraMaxGravityHeight = CAMERA_MAX_GRAVITY_HEIGHT;
		// cameraMaxHeight = CAMERA_MAX_HEIGHT;
		// }

		cameraMaxGravityHeight = CAMERA_MAX_GRAVITY_HEIGHT;
		cameraMaxHeight = CAMERA_MAX_HEIGHT;

		// return to Zone on Z axis
		if (camPos.getZ() < CAMERA_MIN_HEIGHT) {
			camPos.setZ(CAMERA_MIN_HEIGHT);
			camDir.setZ(0);
		}

		if (camPos.getZ() > cameraMaxHeight) {
			camPos.setZ(cameraMaxHeight);
			camDir.setZ(0);
		}

		if (camPos.getZ() < CAMERA_MIN_GRAVITY_HEIGHT) {
			// Log.v(TAG, " tag > min");// (" + CAMERA_MIN_GRAVITY_HEIGHT + ")
			// camPos:" + camPos.toString());
			float power = (CAMERA_MIN_GRAVITY_HEIGHT - camPos.getZ()) * timeFraction * CAMERA_POWER_GRAVITY_SCALE;
			camDir.setZ(camDir.getZ() + power);
		}
		if (camPos.getZ() > cameraMaxGravityHeight) {
			// Log.v(TAG, " tag < max");// (" + cameraMaxGravityHeight + ")
			// camPos:" + camPos.toString());
			float power = (camPos.getZ() - cameraMaxGravityHeight) * timeFraction * CAMERA_POWER_GRAVITY_SCALE;
			camDir.setZ(camDir.getZ() - power);
		}

		// Log.e(TAG, "camPos = " + camPos.toString());
	}

	private void returnCameraToZone() {
		Data data = Data.getInstance();
		Vector3D camPos = data.getCamera().getPosition();
		Vector3D camDir = data.getCamera().getDirection();

		// camPos.setZ(-camPos.getZ());
		Vector2D upLeft = this.camWatchZone.getUpLeft();
		Vector2D downRight = this.camWatchZone.getDownRight();

		// // swap
		// float temp = upLeft.getY();
		// upLeft.setY(downRight.getY());
		// downRight.setY(temp);

		float leftPower = 0;
		float upPower = 0;
		float rightPower = 0;
		float downPower = 0;

		if (upLeft.getX() < camZone.getUpLeft().getX()) {
			leftPower = camZone.getUpLeft().getX() - upLeft.getX();
			if (camDir.getX() > 0)
				camDir.setX(0);
		}

		if (upLeft.getY() > camZone.getUpLeft().getY()) {
			upPower = upLeft.getY() - camZone.getUpLeft().getY();
			if (camDir.getY() < 0)
				camDir.setY(0);
		}

		if (downRight.getX() > camZone.getDownRight().getX()) {
			rightPower = downRight.getX() - camZone.getDownRight().getX();
			if (camDir.getX() < 0)
				camDir.setX(0);
		}

		if (downRight.getY() < camZone.getDownRight().getY()) {
			downPower = camZone.getDownRight().getY() - downRight.getY();
			if (camDir.getY() > 0)
				camDir.setY(0);
		}

		// ////////////////

		if (upLeft.getX() < camZone.getUpLeft().getX() - CAMERA_PERIMETER_BARIER) {
			// camDir.setX(-Math.abs(camDir.getX()) *
			// CAMERA_PROCENT_BOUNCE_WALL);
			// camPos.setX(camPos.getX() + ((camZone.getUpLeft().getX() -
			// CAMERA_PERIMETER_BARIER) - upLeft.getX()));
		}

		if (upLeft.getY() > camZone.getUpLeft().getY() + CAMERA_PERIMETER_BARIER) {
			// camDir.setY(Math.abs(camDir.getY()) *
			// CAMERA_PROCENT_BOUNCE_WALL);
		}

		if (downRight.getX() > camZone.getDownRight().getX() + CAMERA_PERIMETER_BARIER) {
			// camDir.setX(Math.abs(camDir.getX()) *
			// CAMERA_PROCENT_BOUNCE_WALL);
		}

		if (downRight.getY() < camZone.getDownRight().getY() - CAMERA_PERIMETER_BARIER) {
			// camDir.setY(-Math.abs(camDir.getY()) *
			// CAMERA_PROCENT_BOUNCE_WALL);
		}

		// // swap return
		// temp = upLeft.getY();
		// upLeft.setY(downRight.getY());
		// downRight.setY(temp);

		camDir.setX(camDir.getX() + (rightPower - leftPower) * this.timeFraction * CAMERA_POWER_RETURN_TO_ZONE);
		camDir.setY(camDir.getY() + (upPower - downPower) * this.timeFraction * CAMERA_POWER_RETURN_TO_ZONE);

		// String s1 = "   ", s2 = "   ", s3 = "   ", s4 = "   ";
		// if (leftPower != 0) {
		// s1 = " l ";
		// }
		// if (upPower != 0) {
		// s2 = " u ";
		// }
		// if (rightPower != 0) {
		// s3 = " r ";
		// }
		// if (downPower != 0) {
		// s4 = " d ";
		// }
		//
		// Log.v(TAG, s1 + s2 + s3 + s4);

		// /
		// /

		if (leftPower != 0 && rightPower != 0 || upPower != 0 && downPower != 0) {
			float distance = 0;

			if (leftPower != 0 && rightPower != 0)
				distance += (leftPower + rightPower) / 2;

			if (upPower != 0 && downPower != 0)
				distance += (upPower + downPower) / 2;

			if (leftPower != 0 && rightPower != 0 && upPower != 0 && downPower != 0)
				distance /= 2;

			// distance *= timeFraction * CAMERA_AUTO_SCALE_POWER;

			cameraMaxGravityHeight = camPos.getZ() - distance;
			cameraMaxHeight = cameraMaxGravityHeight + (CAMERA_MAX_HEIGHT - CAMERA_MAX_GRAVITY_HEIGHT);
			// cameraMaxGravityHeight -= distance;
			// cameraMaxHeight -= distance;

			if (cameraMaxGravityHeight < CAMERA_MIN_GRAVITY_HEIGHT) {
				cameraMaxGravityHeight = CAMERA_MIN_GRAVITY_HEIGHT + 1f;
				cameraMaxHeight = cameraMaxGravityHeight + (CAMERA_MAX_HEIGHT - CAMERA_MAX_GRAVITY_HEIGHT);
			}

			// Log.v(TAG, "tag cameraMaxGravityHeight = " +
			// cameraMaxGravityHeight);
		} else {
			cameraMaxGravityHeight = CAMERA_MAX_GRAVITY_HEIGHT;
			cameraMaxHeight = CAMERA_MAX_HEIGHT;
		}

		// Log.i(TAG, "maxCamera:" + cameraMaxGravityHeight);

		// cameraMaxGravityHeight = CAMERA_MAX_GRAVITY_HEIGHT;
		// cameraMaxHeight = CAMERA_MAX_HEIGHT;

		// return to Zone on Z axis
		if (camPos.getZ() < CAMERA_MIN_HEIGHT) {
			camPos.setZ(CAMERA_MIN_HEIGHT);
			camDir.setZ(0);
		}

		if (camPos.getZ() > cameraMaxHeight) {
			camPos.setZ(cameraMaxHeight);
			camDir.setZ(0);
		}

		if (camPos.getZ() < CAMERA_MIN_GRAVITY_HEIGHT) {
			// Log.v(TAG, " tag > min");// (" + CAMERA_MIN_GRAVITY_HEIGHT + ")
			// camPos:" + camPos.toString());
			float power = (CAMERA_MIN_GRAVITY_HEIGHT - camPos.getZ()) * timeFraction * CAMERA_POWER_GRAVITY_SCALE;
			camDir.setZ(camDir.getZ() + power);
		}
		if (camPos.getZ() > cameraMaxGravityHeight) {
			// Log.v(TAG, " tag < max");// (" + cameraMaxGravityHeight + ")
			// camPos:" + camPos.toString());
			float power = (camPos.getZ() - cameraMaxGravityHeight) * timeFraction * CAMERA_POWER_GRAVITY_SCALE;
			camDir.setZ(camDir.getZ() - power);
		}
	}

	private void moveCamera() {
		returnCameraToZone();
		// returnCameraToZoneScale();

		Data data = Data.getInstance();

		Vector3D camPos = data.getCamera().getPosition();
		Vector3D camDir = data.getCamera().getDirection();

		if (cameraMoveToTouchObject && getTouchObjectIndex() >= 0) {

			GL_ActiveItem item = data.getTouchItem(getTouchObjectIndex());
			if (item != null) {
				Vector3D objectPos = new Vector3D(item.getCenter());
				objectPos.multy(-1);
				objectPos.minus(camPos);
				objectPos.setZ(0);
				objectPos.divide(1 / CAMERA_SPEED_TO_TOUCH_OBJECT);

				camDir.add(objectPos);
			} else {
				setTouchObjectIndex(-1);
				// data.resetOldItemIndex();
			}

		}
		camDir.setX(camDir.getX() / (1f + CAMERA_MOVE_SLOWER * timeFraction));
		camDir.setY(camDir.getY() / (1f + CAMERA_MOVE_SLOWER * timeFraction));
		camDir.setZ(camDir.getZ() / (1f + CAMERA_SCALE_SLOWER * timeFraction));
		if (Math.abs(camDir.getZ()) > CAMERA_MAX_SCALE_SPEED) {
			if (camDir.getZ() > 0) {
				camDir.setZ(CAMERA_MAX_SCALE_SPEED);
			} else {
				camDir.setZ(-CAMERA_MAX_SCALE_SPEED);
			}
		}
		camPos.setX(camDir.getX() * timeFraction + camPos.getX());
		camPos.setY(camDir.getY() * timeFraction + camPos.getY());
		camPos.setZ(camDir.getZ() * timeFraction + camPos.getZ());

		updateCameraWatchZone();

		// Log.i(TAG, "Camera:" + camPos.toString());
	}

	public void refreshWorld() {
		updateTime();

		Data data = Data.getInstance();

		if (getTouchObjectIndex() > 0) {
			GL_ActiveItem item = data.getTouchItem(getTouchObjectIndex());

			returnNormalBounce();

			if (item != null && item.getScaleValue() > 1f) {
				item.setDirection(0, 0);
				item.setScaleValue(1f);
				// Data.getInstance().setItemReverseScale();
			}
		}
		// returnNormalBounce(this.touchObjectIndex);
		// returnNormalBounce(this.touchObjectIndexCompany);

		camZone.setNull();
		worldBarrier.setRectangle(data.getWorldBarrier());

		// Data.getInstance().updateBackgroundCenter(worldBarrier);

		setTouchObjectIndex(-1);

		// this.touchObjectIndex = -1;
		// this.touchObjectIndexCompany = -1;

		touchObjectMoved = false;
		cameraMoveToTouchObject = false;
		touchObjectPositionFixed = false;
		firstTouchPosition = null;
		lastTouchPosition = null;
	}

	public void followObject(int index) {
		if (index != -1) {
			// touchObjectMoved = true;
			cameraMoveToTouchObject = true;
			touchObjectPositionFixed = true;
			setTouchObjectIndex(index);
		}
	}

	public void setRun(boolean value) {
		this.isRun = value;
	}

	public float getWorldWidth() {
		return widthWorld;
	}

	public float getWorldHeight() {
		return heightWorld;
	}

	public void setWidthWorld(float width) {
		widthWorld = width;
	}

	public void setHeightWorld(float height) {
		heightWorld = height;
	}

	public float getViewRatio() {
		return viewRatio;
	}

	public void setViewRatio(float viewRatio) {
		this.viewRatio = viewRatio;
	}

	public void setScreenSize(int width, int height) {
		this.widthScreen = width;
		this.heightScreen = height;
	}

	public int getTouchObjectIndex() {
		// if (Data.getInstance().isSecondaryCanvas()) {
		// return touchObjectIndexCompany;
		// } else {
		return touchObjectIndex;
		// }
	}

	public void setTouchObjectIndex(int touchObjectIndex) {
		// if (Data.getInstance().isSecondaryCanvas()) {
		// this.touchObjectIndexCompany = touchObjectIndex;
		// } else {
		this.touchObjectIndex = touchObjectIndex;
		// }
	}

	public float getTouchBounceItemValue() {
		// if (Data.getInstance().isSecondaryCanvas()) {
		// return touchBounceItemValueCompany;
		// } else {
		return touchBounceItemValue;
		// }
	}

	public void setTouchBounceItemValue(float value) {
		// if (Data.getInstance().isSecondaryCanvas()) {
		// this.touchBounceItemValueCompany = value;
		// } else {
		this.touchBounceItemValue = value;
		// }
	}

}
